using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;

using EMan.BL.Commands;
using EMan.BL.Properties;
using EMan.Common.EnterpriseStructure;
using EMan.Interface.Common;
using EMan.Interface.EnterpriseStructure.Jobs;

using TrueCode.Core;
using TrueCode.Core.Aspects;
using TrueCode.Core.Diagnostics;
using TrueCode.Core.EditableEntity;
using TrueCode.Core.LifetimeManagement;

namespace EMan.BL.EnterpriseStructure.Jobs
{
  [UsedImplicitly]
  public class JobListEditorPresenter : Presenter< IJobListEditorView >, IJobListEditorPresenter
  {
    private readonly IEnterpriseStructure _eMan;
    private readonly ILifetime _lifetime;
    private ObservableCollection< JobEditable > _jobs;

    private bool _isReadonly;
    private JobEditable _selectedItem;

    private readonly SimpleCommand _createJobCommand;
    private readonly SimpleCommand _editJobCommand;
    private readonly SimpleCommand _deleteJobCommand;
    private readonly SimpleCommand _saveChangesCommand;

    private JobEditable _editJobSession;

    #region .ctors
    public JobListEditorPresenter( [NotNull] IEnterpriseStructure eMan, [NotNull] ILifetime lifetime, [NotNull] IDiagnostic diagnostic )
      : base( diagnostic )
    {
      #region Validation
      if( eMan == null ) throw new ArgumentNullException( "eMan" );
      if( lifetime == null ) throw new ArgumentNullException( "lifetime" );
      #endregion

      _createJobCommand   = new SimpleCommand( CreateJob );
      _editJobCommand     = new SimpleCommand( EditJob, false );
      _deleteJobCommand   = new SimpleCommand( DeleteJob, false );
      _saveChangesCommand = new SimpleCommand( SaveChanges );

      _eMan = eMan;
      _lifetime = lifetime;

      var executor = new ActivityExecutor( this, new Activity( Resources.Common_ActivityLoading_Title, null, true ) );
      executor.ExecuteAsync(
        _eMan.GetAllJobs,
        jobs => Jobs = new ObservableCollection< JobEditable >( jobs.Select( j => new JobEditable( j ) )));
      }
    #endregion

    #region .dtor
    public override void Dispose(){}
    #endregion

    #region Implementation of IJobListEditorPresenter
    public bool IsReadonly
    {
      get { return _isReadonly; }
      private set
      {
        if( _isReadonly != value )
        {
          _isReadonly = value;
          RaisePropertyChanged( () => IsReadonly );
        }
      }
    }

    public JobEditable SelectedItem
    {
      get { return _selectedItem; }
      set
      {
        _selectedItem = value;
        UpdateActionsState();
      }
    }

    ICommand IJobListEditorPresenter.CreateJobCommand
    {
      get { return _createJobCommand; }
    }

    public ICommand EditJobCommand
    {
      get { return _editJobCommand; }
    }

    public ICommand DeleteJobCommand
    {
      get { return _deleteJobCommand; }
    }

    public ICommand SaveChangesCommand
    {
      get { return _saveChangesCommand; }
    }

    public JobEditable EditJobSession
    {
      get { return _editJobSession; }
      private set
      {
        _editJobSession = value;
        RaisePropertyChanged( () => EditJobSession );
      }
    }

    private void CreateJob()
    {
      var jobEditable = new JobEditable( Resources.CreateJob_NewJob, null,
                                         title =>
                                         !_jobs.Any(j => j.Title.Equals( title, StringComparison.CurrentCultureIgnoreCase ) ));

      var onChangesAccepted = new EventHandler<JobEditable, EventArgs>( (e, _) => _jobs.Add( new JobEditable(e, true) ));
      jobEditable.ChangesAccepted += onChangesAccepted;
      _lifetime.Add( () => jobEditable.ChangesAccepted -= onChangesAccepted );

      EditJobSession = jobEditable;
    }


    private void EditJob()
    {
      var selectedJob = _selectedItem;
      var jobEditable = new JobEditable( selectedJob, title => !_jobs.Any( j => j != _selectedItem && j.Title.Equals( title, StringComparison.CurrentCultureIgnoreCase ) ) );
      var onChangesAccepted = new EventHandler<JobEditable, EventArgs>( (e, _) => e.SetValuesTo( selectedJob ) );
      jobEditable.ChangesAccepted += onChangesAccepted;
      _lifetime.Add( () => jobEditable.ChangesAccepted -= onChangesAccepted );
      EditJobSession = jobEditable;
    }

    private void DeleteJob()
    {
      if( _selectedItem.State == EditableObjectState.New )
        _jobs.Remove( _selectedItem );
      else
        _selectedItem.MarkDeleted();
    }

    private void SaveChanges()
    {
      var executor = new ActivityExecutor( this, new Activity( Resources.EditJobs_ActivitySave_Title, null ),
        () => IsReadonly = true,
        () => IsReadonly = false );

        executor.ExecuteAsync( () =>
                               _eMan.UpdateJobs( ChangesetManager.MakeChangeset< JobEditable, Job >( _jobs ) ),
                               result => ChangesetManager.Merge( result, _jobs ) );
    }

    IEnumerable< JobEditable > IJobListEditorPresenter.Jobs
    {
      get { return Jobs; }
    }
    #endregion

    #region Private
    private ObservableCollection< JobEditable > Jobs
    {
      get { return _jobs; }
      set
      {
        _jobs = value;
        RaisePropertyChanged( () => Jobs );
      }
    }

    private void UpdateActionsState()
    {
      _editJobCommand.CanExecute = _selectedItem != null && _selectedItem.State != EditableObjectState.Deleted;
      _deleteJobCommand.CanExecute = _selectedItem != null && _selectedItem.State != EditableObjectState.Deleted;
    }
    #endregion
  }
}